#include <winbuf/winbuf.h>

#define DRV_NAME "winbuf driver"
#define DRV_VERSION "0.1"

#define DEV_NAME "winbuf"

#define UIO_WINBUF_TOP          1
#define UIO_WINBUF_FLUSH        2
#define UIO_WINBUF_INFO         3

NX_PRIVATE NX_Error WinbufOpen(struct NX_Device *device, NX_U32 flags)
{
    Winbuf * wb;
    wb = CreateWinbuf(GetBaseWinbufWidth(), GetBaseWinbufHeight());
    if (wb == NX_NULL)
    {
        NX_Printf("winbuf: create winbuf failed!\n");
        return NX_ENOMEM;
    }
    device->extension = wb;

    if (!NX_StrCmp(device->name, DEV_NAME "0"))
    {
        SetRootWinbuf(wb);
    }

    TopWinbuf(wb); /* top winbuf */
    return NX_EOK;
}

NX_PRIVATE NX_Error WinbufClose(struct NX_Device *device)
{
    if (!NX_StrCmp(device->name, DEV_NAME "0"))
    {
        SetRootWinbuf(NX_NULL);
    }
    return DestroyWinbuf(device->extension);
}

NX_PRIVATE NX_Error WinbufRead(struct NX_Device *device, void *buf, NX_Offset off, NX_Size len, NX_Size *outLen)
{
    Winbuf * wb = (Winbuf *)device->extension;
    WinbufMsg m;
    NX_Error err;

    if (!buf || len != sizeof(m))
    {
        return NX_EINVAL;
    }

    NX_MemZero(&m, sizeof(m));
    
    if ((err = GetWinbufMsg(wb, &m)) != NX_EOK)
    {
        return err;
    }

	void * mBuf = NX_DriverRemapBuf(buf, len);
	if (mBuf == NX_NULL)
	{
		return NX_EPERM;
	}

    NX_MemCopy(mBuf, &m, sizeof(m));

    if (outLen)
    {
		NX_Size * mOutLen = NX_DriverRemapBuf(outLen, sizeof(NX_Size));
		if (mOutLen == NX_NULL)
		{
			return NX_EPERM;
		}

        *mOutLen = len;
    }
    return NX_EOK;
}

NX_PRIVATE NX_Error WinbufControl(struct NX_Device *device, NX_U32 cmd, void *arg)
{
    Winbuf * wb = (Winbuf *)device->extension;
    NX_Error err = NX_EOK;

    NX_ASSERT(wb);

    switch (cmd)
    {
    case UIO_WINBUF_TOP:
        err = TopWinbuf(wb);
        break;

    case UIO_WINBUF_FLUSH:
        {
            if (!arg)
            {
                return NX_EPERM;
            }
#ifndef CONFIG_WINBUF_SHM
            void * mBuf = NX_DriverRemapBuf(arg, wb->width * wb->height * WINBUF_PIXEL_SIZE);
            if (mBuf == NX_NULL)
            {
                return NX_EPERM;
            }

            /* bitblt buf & flush */
            DrawWinbufBlock(wb, mBuf);
#endif
            err = FlushWinbuf(wb, NX_NULL);
        }
        break;
    case UIO_WINBUF_INFO:
        {
            WinbufInfo wi;

            wi.width = wb->width;
            wi.height = wb->height;
            wi.bpp = 8 * WINBUF_PIXEL_SIZE;
            
            WinbufInfo * mwi = NX_DriverRemapBuf(arg, sizeof(NX_Size));
            if (mwi == NX_NULL)
            {
                return NX_EPERM;
            }
            *mwi = wi;
        }
        break;

#ifdef CONFIG_WINBUF_SHM
    case UIO_WINBUF_SHMNAME:
        {
            char * shnName = NX_DriverRemapBuf(arg, NX_SHAREMEM_NAME_LEN);
            if (shnName == NX_NULL)
            {
                return NX_EPERM;
            }
            NX_StrCopyN(shnName, wb->shmName, NX_SHAREMEM_NAME_LEN);
        }
        break;
#endif
    default:
        err = NX_EINVAL;
        break;
    }

    return err;
}

NX_PRIVATE NX_DriverOps WinbufDriverOps = {
    .open       = WinbufOpen,
    .close      = WinbufClose,
    .read       = WinbufRead,
    .control    = WinbufControl
};

NX_PRIVATE NX_Error WinbufDriverInit(NX_Solt * outDriverSolt, NX_Driver **outDriver)
{
	NX_Device *device;
    NX_Solt driverSolt = NX_SOLT_INVALID_VALUE;

    NX_Driver *driver = NX_DriverCreate(DRV_NAME, NX_DEVICE_TYPE_VIRT, 0, &WinbufDriverOps);
    if (driver == NX_NULL)
    {
        NX_Printf("create driver failed!\n");
        return NX_ENOMEM;
    }
    NX_Printf("create winbuf driver ok.\n");

    int i;
    for (i = 0; i < CONFIG_WINBUF_DEV_NR; i++)
    {
        char name[NX_DEVICE_NAME_LEN] = {0};
        NX_StrPrintfN(name, NX_DEVICE_NAME_LEN, "%s%d", DEV_NAME, i);
        if (NX_DriverAttachDevice(driver, name, &device) != NX_EOK)
        {
            NX_Printf("attach device %s failed!\n", name);
            NX_DriverDestroy(driver);
            return NX_ENOMEM;
        }
    }
    NX_Printf("add winbuf device ok.\n");

    if ((driverSolt = NX_DriverRegister(driver)) == NX_SOLT_INVALID_VALUE)
    {
        NX_Printf("driver register failed!\n");
        NX_DriverDestroy(driver);
        return NX_ERROR;
    }
    NX_Printf("register winbuf driver ok.\n");

    *outDriver = driver;
    
    return NX_EOK;
}

NX_Error WinbufRunUdriver(void)
{
    NX_Error err;
    NX_Solt driverSolt = NX_SOLT_INVALID_VALUE;
    NX_Driver *driver = NX_NULL;

    if ((err = WinbufDriverInit(&driverSolt, &driver)) != NX_EOK)
    {
        NX_Printf("init keyboad failed with err %s!\n", NX_ErrorToString(err));
        return err;
    }
    
    NX_Printf("ready run winbuf udriver\n");
    err = NX_DriverRun(driverSolt, driver);
    NX_Printf("driver run state %s\n", NX_ErrorToString(err));

    return NX_EOK;
}
